package org.paboo.chart.pdf417;

import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;

import org.paboo.util.LoggerFactoryUtils;
import org.paboo.util.ParameterObject;
import org.paboo.util.ZxingImage;
import org.paboo.util.ZxingValidation;
import org.paboo.http.BaseServlet;
import org.paboo.http.HttpMethod;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

public class Pdf417Servlet extends BaseServlet {

    private static final long serialVersionUID = 2978803208721169289L;
    private LoggerFactoryUtils log = LoggerFactoryUtils.getInstance().load(getClass());

    @Override
    protected void processRequest(HttpServletRequest request, HttpServletResponse response, HttpMethod method) throws ServletException, IOException {
        Pdf417Entity p = new Pdf417Entity();
        if(method == HttpMethod.GET) {
            String ctx = getParameterWithDefault(request, "data", ParameterObject.DEFAULT_CONTECT);
            String encoding = getParameterWithDefault(request, "encoding", StandardCharsets.ISO_8859_1.name());
            ctx = URLDecoder.decode(ctx, encoding);
            log.debug(">>>" + ctx + " - " + encoding);
            p.setData(ctx);
            p.setEncoding(encoding);

            int ecLevel = getIntParameterWithDefault(request, "eclevel", 2);
            if (ecLevel >= 0 && ecLevel <= 8) {
                p.setErrorCorrectionLevel(ecLevel);
            } else {
                String errorMsg = "Data parameter ERROR!";
                log.warn(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }

            String format = getParameterWithDefault(request, "format", "png");
            if (!ZxingValidation.checkFormat(format.toLowerCase()) || "text".equalsIgnoreCase(format)) {
                String errorMsg = "Data parameter ERROR!";
                log.warn(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }
            p.setFormat(format.toLowerCase());

            String size = getParameterWithDefault(request, "size", "300x100");
            if (size.contains("x")) {
                String[] sizea = size.split("x");
                if (ZxingValidation.isNum(sizea[0]) && ZxingValidation.isNum(sizea[1])) {
                    p.setWidth(Integer.parseInt(sizea[0]));
                    p.setHeight(Integer.parseInt(sizea[1]));
                } else {
                    String errorMsg = "Data parameter ERROR!";
                    log.warn(errorMsg);
                    response.setContentType("text/plain");
                    response.getOutputStream().print(errorMsg);
                    return;
                }
            } else {
                String errorMsg = "Date parameter ERROR!";
                log.warn(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }

            int margin = getIntParameterWithDefault(request, "margin", 5);
            p.setMargin(margin);
        } else if (method == HttpMethod.POST) {
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(request.getInputStream(), request.getCharacterEncoding()));
            Gson gson = new Gson();
            try {
                p = gson.fromJson(br, Pdf417Entity.class);
            } catch (JsonIOException | JsonSyntaxException ex) {
                String errorMsg = "Data parameter ERROR!";
                log.error(errorMsg);
                response.setContentType("text/plain");
                response.getOutputStream().print(errorMsg);
                return;
            }

            if (p == null) {
                p = new Pdf417Entity();
            }
            if (!ParameterObject.hasText(p.getData())) {
                p.setData(ParameterObject.DEFAULT_CONTECT);
            }
            if (!ParameterObject.hasText(p.getEncoding()) || "null".equalsIgnoreCase(p.getEncoding())) {
                p.setEncoding(StandardCharsets.ISO_8859_1.name());
            }
            if (p.getErrorCorrectionLevel() == null) {
                p.setErrorCorrectionLevel(2);
            }
            if (!ParameterObject.hasInteger(p.getMargin())) {
                p.setMargin(30);
            }
            if (!ParameterObject.hasInteger(p.getWidth()) || !ParameterObject.hasInteger(p.getHeight())) {
                p.setWidth(300);
                p.setHeight(100);
            }
            if (!ParameterObject.hasText(p.getFormat()) ||
                    !ZxingValidation.checkFormat(p.getFormat().toLowerCase()) ||
                    "text".equalsIgnoreCase(p.getFormat())) {
                p.setFormat("png");
            }
            log.info(gson.toJson(p));
        } else {
            response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, "Request error!!!");
            return;
        }

        BitMatrix matrix;
        try {
            matrix = ZxingUtil.encode(p);
            log.debug("Matrix size:" + matrix.getWidth() + "x" + matrix.getHeight());
        } catch (WriterException e) {
            String errorMsg = "Create Fail!!!";
            if (log.isDebugEnabled()) {
                log.error(errorMsg, e);
            } else {
                log.error(errorMsg);
            }
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorMsg);
            return;
        }

        if ("svg".equalsIgnoreCase(p.getFormat())) {
            try {
                response.setContentType("image/svg+xml");
                ZxingImage.toSVGImage(matrix,
                        response.getOutputStream(),
                        ZxingImage.WHITE,
                        ZxingImage.BLACK);
            } catch (Exception ex) {
                String errorMsg = "Create Fail!!!";
                if (log.isDebugEnabled()) {
                    log.error(errorMsg, ex);
                } else {
                    log.error(errorMsg);
                }
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorMsg);
            }
            return;
        }

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        if (!ImageIO.write(ZxingImage.toBufferedImage(matrix, ZxingImage.WHITE, ZxingImage.BLACK), p.getFormat(), out)) {
            log.error("Could not write an image of format " + p.getFormat());
            throw new IOException("Could not write an image of format " + p.getFormat());
        }
        response.setContentType("image/" + p.getFormat());
        response.setContentLength(out.size());
        response.getOutputStream().write(out.toByteArray());
    }
}
